Skip to content

S11-12 Vue-项目:mr-vue3-ts-consult-patient2

[TOC]

Consult

image-20250312175823339

image-20250312175839658

image-20250312180136818

TS类型

知识点Enum

1、使用枚举类型定义问诊类型、问诊时间

ts
// 问诊类型
export enum ConsultType {
  /** 找医生 */
  Doctor = 1,
  /** 快速问诊 */
  Fast = 2,
  /** 开药问诊 */
  Medication = 3
}
// 问诊时间,以1自增可以省略
export enum IllnessTime {
  /** 一周内 */
  Week = 1,
  /** 一月内 */
  Month,
  /** 半年内 */
  HalfYear,
  /** 半年以上 */
  More
}

2、定义问诊记录类型

ts
import { ConsultType, IllnessTime } from '@/enums'

// 图片列表
export type Image = {
  /** 图片ID */
  id: string
  /** 图片地址 */
  url: string
}
// 问诊记录
export type Consult = {
  /** 问诊记录ID */
  id: string
  /** 问诊类型 */
  type: ConsultType
  /** 快速问诊类型,0 普通 1 三甲 */
  illnessType: 0 | 1
  /** 科室ID */
  depId: string
  /** 疾病描述 */
  illnessDesc: string
  /** 疾病持续时间 */
  illnessTime: IllnessTime
  /** 是否就诊过,0 未就诊过  1 就诊过 */
  consultFlag: 0 | 1
  /** 图片数组 */
  pictures: Image[]
  /** 患者ID */
  patientId: string
  /** 优惠券ID */
  couponId: string
}

// 问诊记录-全部可选
export type PartialConsult = Partial<Consult>
// Required 转换为全部必须   Partial 转换问全部可选  两个内置的泛型类型

问诊类型

image-20250312214932564

定义问诊仓库

1、在 store/consult.ts 中定义 consultStore

image-20250312215553677

2、在 store/index.ts 中统一导出consultStore

image-20250312215622172

记录问诊类型

1、在 store/consult.ts 中定义 setType() 方法,记录问诊类型

image-20250312215907293

2、在 home 组件调用 setType() 方法,记录问诊类型

image-20250312220022460

问诊级别:consult-fast

image-20250312220737249

image-20250312220153218

路由规则

image-20250312220447274

页面布局

1、HTML

html
<template>
  <div class="consult-fast-page">
    <cp-nav-bar title="极速问诊" right-text="问诊记录"></cp-nav-bar>
    <div class="fast-logo">
      <img class="img" src="@/assets/consult-fast.png" alt="" />
      <p class="text"><span>20s</span> 快速匹配专业医生</p>
    </div>
    <div class="fast-type">
      <router-link to="/consult/dep" class="item">
        <cp-icon class="pic" name="consult-doctor"></cp-icon>
        <div class="info">
          <p>三甲图文问诊</p>
          <p>三甲主治及以上级别医生</p>
        </div>
        <van-icon name="arrow"></van-icon>
      </router-link>
      <router-link to="/consult/dep" class="item">
        <cp-icon class="pic" name="consult-message"></cp-icon>
        <div class="info">
          <p>普通图文问诊</p>
          <p>二甲主治及以上级别医生</p>
        </div>
        <van-icon name="arrow"></van-icon>
      </router-link>
    </div>
  </div>
</template>

2、样式

less
.consult-fast-page {
  padding-top: 46px;
  .fast-logo {
    padding: 30px 0;
    text-align: center;
    .img {
      width: 240px;
    }
    .text {
      font-size: 16px;
      margin-top: 10px;
      > span {
        color: var(--cp-primary);
      }
    }
  }
  .fast-type {
    padding: 15px;
    .item {
      display: flex;
      padding: 16px;
      border-radius: 4px;
      align-items: center;
      margin-bottom: 16px;
      border: 0.5px solid var(--cp-line);
    }
    .pic {
      width: 40px;
      height: 40px;
    }
    .info {
      margin-left: 12px;
      flex: 1;
      > p:first-child {
        font-size: 16px;
        color: var(--cp-text1);
        margin-bottom: 4px;
      }
      > p:last-child {
        font-size: 13px;
        color: var(--cp-tag);
      }
    }
    .van-icon {
      color: var(--cp-tip);
    }
  }
}

记录问诊级别

1、在 store/consult.ts 中定义 setIllnessType() 方法,记录问诊级别

image-20250312221018269

2、在 consult-fast 组件调用 setIllnessType() 方法,记录问诊级别

image-20250312221136750

问诊科室:consult-department

image-20250312221241332

image-20250312221435862

路由规则

image-20250312221700726

页面布局

1、HTML

html
<template>
  <div class="consult-dep-page">
    <!-- 导航栏 -->
    <cp-nav-bar title="选择科室" />
    <div class="wrapper">
      <!-- 一级科室 -->
      <van-sidebar v-model="active">
        <van-sidebar-item title="内科" />
        <van-sidebar-item title="外科" />
        <van-sidebar-item title="皮肤科" />
        <van-sidebar-item title="骨科" />
      </van-sidebar>
      <!-- 二级科室 -->
      <div class="sub-dep">
        <router-link to="/consult/illness">科室一</router-link>
        <router-link to="/consult/illness">科室二</router-link>
        <router-link to="/consult/illness">科室三</router-link>
      </div>
    </div>
  </div>
</template>

2、样式

less
.van-sidebar {
  width: 114px;
  &-item {
    padding: 14px;
    color: var(--cp-tag);
    &--select {
      color: var(--cp-main);
      font-weight: normal;
      &::before {
        display: none;
      }
    }
  }
}
.consult-dep-page {
  padding-top: 46px;
  .wrapper {
    height: calc(100vh - 46px);
    overflow: hidden;
    display: flex;
    .sub-dep {
      flex: 1;
      height: 100%;
      overflow-y: auto;
      > a {
        display: block;
        padding: 14px 30px;
        color: var(--cp-dark);
      }
    }
  }
}

渲染请求数据

TS类型
ts
// 二级科室
export type SubDep = {
  /** 科室ID */
  id: string
  /** 科室名称 */
  name: string
}

// 一级科室
export type TopDep = SubDep & {
  /** 二级科室数组 */
  child: SubDep[]
}
接口
  • URL/dep/all

  • 类型GET

  • token:携带

  • 参数:无

  • 返回数据

    image-20250312222322921

渲染数据

1、在 services/consult.ts 中发送网络请求

image-20250312222744813

2、在组件中调用请求方法,获取一级科室列表数据

image-20250312222912309

3、渲染一级科室数据

image-20250312223334505

4、根据 active 属性,获取二级科室列表数据

image-20250312223256613

5、渲染二级科室数据

image-20250312223418582

记录问诊科室

1、在 store/consult.ts 中定义 setDep() 方法,记录问诊科室

image-20250312223619113

2、在 consult-department 组件调用 setDep() 方法,记录问诊科室

image-20250312223857452

病情描述:consult-illness

image-20250313085619022

image-20250313085928993

路由规则

image-20250313090034901

页面布局

1、HTML

html
<template>
  <div class="consult-illness-page">
    <cp-nav-bar title="图文问诊" />
    <!-- 医生提示 -->
    <div class="illness-tip van-hairline--bottom">
      <img class="img" src="@/assets/avatar-doctor.svg" />
      <div class="info">
        <p class="tit">在线医生</p>
        <p class="tip">
          请描述你的疾病或症状、是否用药、就诊经历,需要我听过什么样的帮助
        </p>
        <p class="safe">
          <cp-icon name="consult-safe" /><span>内容仅医生可见</span>
        </p>
      </div>
    </div>
    <!-- 收集信息 -->
    <div class="illness-form">
      <van-field
        type="textarea"
        rows="3"
        placeholder="请详细描述您的病情,病情描述不能为空"
      ></van-field>
      <div class="item">
        <p>本次患病多久了?</p>
      </div>
      <div class="item">
        <p>此次病情是否去医院就诊过?</p>
      </div>
    </div>
  </div>
</template>

2、样式

less
.consult-illness-page {
  padding-top: 46px;
}
.illness-tip {
  display: flex;
  padding: 15px;
  .img {
    width: 52px;
    height: 52px;
    border-radius: 4px;
    overflow: hidden;
    margin-top: 10px;
  }
  .info {
    flex: 1;
    padding-left: 12px;
    .tit {
      font-size: 16px;
      margin-bottom: 5px;
    }
    .tip {
      padding: 12px;
      background: var(--cp-bg);
      color: var(--cp-text3);
      font-size: 13px;
      margin-bottom: 10px;
    }
    .safe {
      font-size: 10px;
      color: var(--cp-text3);
      display: flex;
      align-items: center;
      .cp-icon {
        font-size: 12px;
        margin-right: 2px;
      }
    }
  }
}
.illness-form {
  padding: 15px;
  .van-field {
    padding: 0;
    &::after {
      border-bottom: none;
    }
  }
  .item {
    > p {
      color: var(--cp-text3);
      padding: 15px 0;
    }
  }
}

表单数据

1、TS类型

ts
export type ConsultIllness = Pick<
  PartialConsult,
  'illnessDesc' | 'illnessTime' | 'consultFlag' | 'pictures'
>

2、表单数据

ts
import type { ConsultIllness } from '@/types/consult'
import { ref } from 'vue'
import { IllnessTime } from '@/enums'

const timeOptions = [
  { label: '一周内', value: IllnessTime.Week },
  { label: '一月内', value: IllnessTime.Month },
  { label: '半年内', value: IllnessTime.HalfYear },
  { label: '半年以上', value: IllnessTime.More }
]
const flagOptions = [
  { label: '就诊过', value: 1 },
  { label: '没就诊过', value: 0 }
]
const form = ref<ConsultIllness>({
  illnessDesc: '',
  illnessTime: undefined,
  consultFlag: undefined,
  pictures: []
})

渲染数据

image-20250313090807790

上传图片@

image-20250313090923115

image-20250313091300966

页面布局

1、HTML

html
      <div class="illness-img">
        <van-uploader></van-uploader>
        <p class="tip" >上传内容仅医生可见,最多9张图,最大5MB</p>
      </div>

2、样式

less
.illness-img {
  padding-top: 16px;
  margin-bottom: 40px;
  display: flex;
  align-items: center;
  .tip {
    font-size: 12px;
    color: var(--cp-tip);
  }
  ::v-deep() {
    .van-uploader {
      &__preview {
        &-delete {
          left: -6px;
          top: -6px;
          border-radius: 50%;
          background-color: var(--cp-primary);
          width: 20px;
          height: 20px;
          &-icon {
            transform: scale(0.9) translate(-22%, 22%);
          }
        }
        &-image {
          border-radius: 8px;
          overflow: hidden;
        }
      }
      &__upload {
        border-radius: 8px;
      }
      &__upload-icon {
        color: var(--cp-text3);
      }
    }
  }
}
配置图片文字

image-20250313091420287

限制数量和大小

image-20250313091520456

收集图片数据

image-20250313091817932

实现上传业务

image-20250313092316157

接口
  • URL/upload

  • 类型POST

  • token:携带

  • 参数

    ts
    {
      file: FormData // 上传的文件
    }
  • 返回数据

    image-20250313092507137

上传图片

1、在 services/consult.ts 中发送网络请求

image-20250313092910288

2、在组件中,通过after-read绑定图片上传完毕后的回调函数

image-20250313093040343

image-20250313093249530

3、通过after-read的item.status参数定义上传状态

image-20250313093737007

4、同步上传数据到form.pictures中

image-20250313094159109

3、在组件中,通过@delete监听删除文件预览的处理函数

image-20250313094548561

记录病情描述

image-20250313094818625

按钮激活

1、默认情况下按钮有disabled类,此时按钮为灰色状态

image-20250313095359180

2、样式默认为灰色

less
.van-button {
  font-size: 16px;
  margin-bottom: 30px;
  &.disabled {
    opacity: 1;
    background: #fafafa;
    color: #d9dbde;
    border: #fafafa;
  }
}

3、激活:当病情描述、时间、是否就诊选中时激活按钮

image-20250313100000655

image-20250313100027123

轻提示校验

image-20250313100255265

image-20250313100308618

记录病情描述

1、在 store/consult.ts 中定义记录病情描述的方法 setIllness()

image-20250313100559367

2、在组件中调用 setIllness() 方法,记录病情描述,并跳转到选择患者页面

image-20250313100844841

回显数据

image-20250313101204359

image-20250313101251327

1、当进入到病情描述页面时,判断是否已经存在病情描述数据,如果存在则回显该数据。

image-20250313101728108

2、通过设置closeOnPopstate: false处理从选择患者页面回退时,无法弹出弹框的问题

image-20250313102133254

选择患者:patient

image-20250313102340081

image-20250313102343482

界面兼容

依据isChange=1区分选择患者、家庭档案

image-20250313102952407

选中患者效果

1、在患者项上绑定点击事件,设置选中患者id

image-20250313103620662

image-20250313103447795

2、阻止点击编辑按钮时的事件冒泡

image-20250313103551153

默认选中

思路:进入页面后,如果有默认患者,则选中该患者;如果没有默认患者,则选中第一个患者。

image-20250313104048231

记录患者ID

1、在 store/consult.ts 中,添加记录患者ID的方法 setPatient()

image-20250313104154482

2、在组件中点击下一步时,调用 setPatient() 方法记录患者ID

image-20250313104235521

image-20250313104414499

问诊支付:consult-pay

image-20250313104552040

image-20250313114916442

路由规则

image-20250313115132503

页面布局

1、HTML

html
<template>
  <div class="consult-pay-page">
    <cp-nav-bar title="支付" />
    <div class="pay-info">
      <p class="tit">图文问诊 49 元</p>
      <img class="img" src="@/assets/avatar-doctor.svg" />
      <p class="desc">
        <span>极速问诊</span>
        <span>自动分配医生</span>
      </p>
    </div>
    <van-cell-group>
      <van-cell title="优惠券" value="-¥10.00" />
      <van-cell title="积分抵扣" value="-¥10.00" />
      <van-cell title="实付款" value="¥29.00" class="pay-price" />
    </van-cell-group>
    <div class="pay-space"></div>
    <van-cell-group>
      <van-cell title="患者信息" value="李富贵 | 男 | 30岁"></van-cell>
      <van-cell title="病情描述" label="头痛,头晕,恶心"></van-cell>
    </van-cell-group>
    <div class="pay-schema">
      <van-checkbox>我已同意 <span class="text">支付协议</span></van-checkbox>
    </div>
    <van-submit-bar button-type="primary" :price="2900" button-text="立即支付" text-align="left" />
  </div>
</template>

2、样式

less
.consult-pay-page {
  padding: 46px 0 50px 0;
}
.pay-info {
  display: flex;
  padding: 15px;
  flex-wrap: wrap;
  align-items: center;
  .tit {
    width: 100%;
    font-size: 16px;
    margin-bottom: 10px;
  }
  .img {
    margin-right: 10px;
    width: 38px;
    height: 38px;
    border-radius: 4px;
    overflow: hidden;
  }
  .desc {
    flex: 1;
    > span {
      display: block;
      color: var(--cp-tag);
      &:first-child {
        font-size: 16px;
        color: var(--cp-text2);
      }
    }
  }
}
.pay-price {
  ::v-deep() {
    .vam-cell__title {
      font-size: 16px;
    }
    .van-cell__value {
      font-size: 16px;
      color: var(--cp-price);
    }
  }
}
.pay-space {
  height: 12px;
  background-color: var(--cp-bg);
}
.pay-schema {
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
  .text {
    color: var(--cp-primary);
  }
}
::v-deep() {
  .van-submit-bar__button {
    font-weight: normal;
    width: 160px;
  }
}
.pay-type {
  .amount {
    padding: 20px;
    text-align: center;
    font-size: 16px;
    font-weight: bold;
  }
  .btn {
    padding: 15px;
  }
  .van-cell {
    align-items: center;
    .cp-icon {
      margin-right: 10px;
      font-size: 18px;
    }
    .van-checkbox :deep(.van-checkbox__icon) {
      font-size: 16px;
    }
  }
}

渲染请求数据

TS类型
ts
// 问诊订单预支付传参
export type ConsultOrderPreParams = Pick<PartialConsult, 'type' | 'illnessType'>

// 问诊订单预支付信息
export type ConsultOrderPreData = {
  /** 积分抵扣 */
  pointDeduction: number
  /** 优惠券抵扣 */
  couponDeduction: number
  /** 优惠券ID */
  couponId: string
  /** 需付款 */
  payment: number
  /** 实付款 */
  actualPayment: number
}
接口-预支付信息
  • URL/patient/consult/order/pre

  • 类型GET

  • token:携带

  • 参数

    ts
    {
      // 急速问诊只需要以下2个属性
      type: string // 问诊类型:1 找医生,2 极速问诊,3 开药问诊
      illnessType: string // 极速问诊级别:0 普通,1 三甲
      
      useCoupon: string // 是否使用优惠券:0 不使用优惠券,1 使用优惠券(默认,且按最大优惠券使用)。
      couponId: string // 可使用优惠券id
      docId: string // 医生id,当type为1时必传
    }
  • 返回数据

    image-20250313120004351

接口-患者信息
  • URL/patient/info/:id

  • 类型GET

  • token:携带

  • 参数

    ts
    {
      id: string // 患者id
    }
  • 返回数据

    image-20250313120729659

渲染数据

1、在 services/consult.ts 中发送网络请求

image-20250313120413162

2、在 services/patient.ts 中发送网络请求

image-20250313120820160

3、在组件中,调用请求方法

image-20250313121148430

image-20250313121339279

4、渲染数据

image-20250313122347297

记录优惠券

1、在store中定义 setCoupon() 方法记录优惠券

image-20250313122540959

2、在组件中,获取到预支付信息后,调用 setCoupon() 方法记录优惠券

image-20250313122648833

骨架屏@

image-20250313122956324

image-20250313123012490

实现支付

支付流程

image-20250313123335722

image-20250313123346032

生成订单

image-20250313123903246

支付抽屉面板

1、在 van-submit-bar 上绑定点击事件,打开支付抽屉面板

image-20250313124336693

image-20250313124351991

2、支付抽屉面板布局

image-20250313124520146

接口
  • URL/patient/consult/order

  • 类型POST

  • token:携带

  • 参数

    ts
    Consult
  • 返回数据

    image-20250313125448132

实现生成订单

1、选择支付方式

image-20250313125226217

2、在 services/consult.ts 中发送网络请求

image-20250313125656157

3、在组件中调用请求方法,获取订单id,同时添加loading效果

image-20250313130020374

image-20250313130324668

清理问诊数据

1、在store中定义 clear() 方法,清理问诊数据

image-20250313130118592

2、在组件中调用 clear() 方法,清理问诊数据

image-20250313130242618

用户引导

image-20250313130714618

阻止返回上页

image-20250313131447145

阻止关闭抽屉

1、设置属性阻止关闭抽屉

image-20250313131152274

2、监听用户点击灰色区域关闭抽屉,弹出确认弹框

image-20250313131341250

image-20250313131736289

刷新页面提示

image-20250313144610831

思路:刷新页面后在onMounted钩子中校验所有需要的key对应的值是否为undefined,如果所有值都不是undefined,则校验通过,否则弹出提示框,提示数据不完整。

image-20250313144702121

实现支付
接口
  • URL/patient/consult/pay

  • 类型POST

  • token:携带

  • 参数

    ts
    {
      paymentMethod: string // 支付方式:0 微信支付,1 支付宝,2 云闪付
      orderId: string // 订单id
      payCallback: string // 回跳地址,http://域名/回跳页面
    }
  • 返回数据

    image-20250313124951980

实现支付

1、在 services/consult.ts 中发送网络请求

image-20250313145554602

2、在组件中调用请求方法,获取支付地址

image-20250313145638533

image-20250313150102211

处理支付失败

思路:支付完成后等待几秒会跳转到指定地址,url参数中包括支付结果payResult,支付成功payResult为true,支付失败payResult为false。

在room路由的beforeEnter路由钩子中处理支付失败的跳转

image-20250313150816503

问诊订单:consult-order

image-20250325173628496

image-20250325173633412

路由规则

image-20250325174615325

页面布局

1、HTML

html
<script setup lang="ts">
import ConsultList from './components/ConsultList.vue'
</script>

<template>
  <div class="consult-page">
    <cp-nav-bar title="问诊记录" />
    <van-tabs sticky>
      <van-tab title="极速问诊"><consult-list /></van-tab>
      <van-tab title="找医生"><consult-list /></van-tab>
      <van-tab title="开药问诊"><consult-list /></van-tab>
    </van-tabs>
  </div>
</template>

2、样式

less
.consult-page {
  padding-top: 46px;
  background-color: var(--cp-bg);
  min-height: calc(100vh - 46px);
}

功能

问诊记录入口

1、入口一:在consult-fast组件中,点击问诊记录跳转

image-20250325174858502

2、入口二:在user组件中,点击问诊记录跳转

接口-问诊记录订单列表
  • URL/patient/consult/order/list

  • 类型GET

  • token:携带

  • 参数

    ts
    {
      current: string // 当前页数,默认:1
      pageSize: string // 每页记录数,默认:10
      type: string // 问诊类型,1:问医生,2:急速问诊,3:开药问诊,默认:1
    }
  • 返回数据

    image-20250325180239979

    image-20250325180253877

接口-TS类型

1、接口参数类型

ts
export type ConsultOrderListParams = PageParams & {
  type: ConsultType // 问诊记录类型
}

2、带分页问诊订单类型

ts
export type ConsultOrderPage = {
  pageTotal: number
  total: number
  rows: ConsultOrderItem[]
}
请求数据

1、在 services/consult.js 中,发送网络请求,获取问诊记录订单列表数据

image-20250325180800404

2、在 consult-record-list 组件中,使用 van-list 组件触发加载更多,并在@load事件处理函数中调用请求方法

image-20250326212823371

image-20250326213139927

3、渲染请求到的问诊记录列表数据

image-20250326213225544

组件:consult-order-list

页面布局

1、HTML

html
<script setup lang="ts">
import ConsultRecordItem from './ConsultRecordItem.vue'
</script>

<template>
  <div class="consult-list">
    <consult-record-item v-for="i in 5" :key="i" />
  </div>
</template>

2、样式

less
.consult-list {
  padding: 10px 15px;
}
接收问诊类型

1、在consult-record 组件中,传递问诊类型属性type

image-20250325175236258

2、在 consult-record-list 组件中,接收属性

image-20250325175322574

组件:consult-order-item

image-20250326214145745

页面布局

1、HTML

html
<template>
  <div class="consult-record-item">
    <div class="head van-hairline--bottom">
      <img class="img" src="@/assets/avatar-doctor.svg" />
      <p>极速问诊(自动分配医生)</p>
      <span>待支付</span>
    </div>
    <div class="body">
      <div class="body-row">
        <div class="body-label">病情描述</div>
        <div class="body-value">腹痛腹泻 胃部有些痉挛</div>
      </div>
      <div class="body-row">
        <div class="body-label">价格</div>
        <div class="body-value">¥ 39.00</div>
      </div>
      <div class="body-row">
        <div class="body-label">创建时间</div>
        <div class="body-value tip">2019-07-08 09:55:54</div>
      </div>
    </div>
    <div class="foot">
      <van-button class="gray" plain size="small" round>取消问诊</van-button>
      <van-button type="primary" plain size="small" round >去支付</van-button>
    </div>
  </div>
</template>

2、样式

less
.consult-record-item {
  border-radius: 4px;
  box-shadow: 0px 0px 22px 0px rgba(245, 245, 245, 0.1);
  background-color: #fff;
  margin-bottom: 10px;
  .head {
    display: flex;
    align-items: center;
    height: 50px;
    padding: 0 15px;
    .img {
      width: 20px;
      height: 20px;
    }
    > p {
      flex: 1;
      font-size: 15px;
      padding-left: 10px;
    }
    > span {
      color: var(--cp-tag);
      &.orange {
        color: #f2994a;
      }
      &.green {
        color: var(--cp-primary);
      }
    }
  }
  .body {
    padding: 15px 15px 8px 15px;
    .body-row {
      display: flex;
      margin-bottom: 7px;
    }
    .body-label {
      width: 62px;
      font-size: 13px;
      color: var(--cp-tip);
    }
    .body-value {
      width: 250px;
      &.tip {
        color: var(--cp-tip);
      }
    }
  }
  .foot {
    padding: 0 15px 15px 15px;
    display: flex;
    justify-content: flex-end;
    align-items: center;
    .van-button {
      margin-left: 10px;
      padding: 0 16px;
      &.gray {
        color: var(--cp-text3);
        background-color: var(--cp-bg);
      }
    }
    .more {
      color: var(--cp-tag);
      flex: 1;
      font-size: 13px;
    }
  }
}
渲染组件

1、渲染卡片头部和主体

image-20250326215316783

2、渲染卡片底部按钮

image-20250326220344215

image-20250326220505959

功能-更多操作

1、使用 van-popover 组件实现更多气泡

image-20250326221228921

2、添加选择事件 @select

image-20250326221414665

image-20250326221625430

3、设置弹出位置为 top-start

image-20250326221445024

image-20250326221504746

功能-取消问诊
接口-取消问诊订单
  • URL/patient/order/cancel/${id}

  • 类型PUT

  • token:携带

  • 参数

    ts
    {
      id: string // 订单id
    }
  • 返回数据

    image-20250326221741309

实现取消问诊

1、在 services/consult.js 中发送网络请求

image-20250326221937579

2、在 consult-record-item 组件中,实现取消订单

image-20250326222346649

3、为所有的 取消问诊 按钮绑定点击事件

image-20250326222421080

功能-删除订单
接口-删除订单
  • URL/patient/order/${id}

  • 类型DELETE

  • token:携带

  • 参数

    ts
    {
      id: string // 订单id
    }
  • 返回数据

    image-20250326222750444

实现删除订单

1、在 services/consult.js 中发送网络请求

image-20250326223256678

2、在 consult-record-item 组件中,实现删除订单,并发射 on-delete 事件到父组件

image-20250326223543252

3、为所有的 删除订单 按钮绑定点击事件

image-20250326223645574

4、更多操作 中也有删除订单按钮,需要在onSelect()方法中调用deleteConsultOrder()方法实现删除订单

image-20250326223929405

5、在父组件 consult-order-list 中,接收发射过来的事件 on-delete,并绑定到onDelete()中实现删除对应的item订单数据

image-20250326224352949

image-20250326224320415

功能-查看处方

1、封装 room-message 组件中的查看处方方法到hooks中

image-20250327153122652

2、使用封装的方法

image-20250327153140373

image-20250327153234510

3、在更多操作的@select中使用封装的方法

image-20250327153351677

问诊详情:consult-detail

image-20250407215932850

image-20250407215953006

路由规则

image-20250407220126319

页面布局

1、HTML

html
<script setup lang="ts"></script>

<template>
  <div class="consult-detail-page">
    <cp-nav-bar title="问诊详情" />
      
    <!-- 头部 -->
    <div class="detail-head">
      <div class="text">
        <h3>图文问诊 39 元</h3>
        <span class="sta green">待支付</span>
        <p class="tip">服务医生信息</p>
      </div>
      <div class="card">
        <img class="avatar" src="@/assets/avatar-doctor.svg" alt="" />
        <p class="doc">
          <span>极速问诊</span>
          <span>自动分配医生</span>
        </p>
        <van-icon name="arrow" />
      </div>
    </div>
      
    <!-- 患者信息 -->
    <div class="detail-patient">
      <van-cell-group :border="false">
        <van-cell title="患者信息" value="李富贵 | 男 | 30岁" />
        <van-cell title="患病时长" value="一周内" />
        <van-cell title="就诊情况" value="未就诊过" />
        <van-cell title="病情描述" label="头痛,头晕,恶心" />
      </van-cell-group>
    </div>
      
    <!-- 订单信息 -->
    <div class="detail-order">
      <h3>订单信息</h3>
      <van-cell-group :border="false">
        <van-cell title="订单编号">
          <template #value>
            <span class="copy">复制</span>
            202201127465
          </template>
        </van-cell>
        <van-cell title="创建时间" value="2022-01-23 09:23:46" />
        <van-cell title="应付款" value="¥39" />
        <van-cell title="优惠券" value="-¥0" />
        <van-cell title="积分抵扣" value="-¥0" />
        <van-cell title="实付款" value="¥39" class="price" />
      </van-cell-group>
    </div>
    <!-- <div class="detail-time">
      请在 <van-count-down :time="10000 * 1000" /> 内完成支付,超时订单将取消
    </div> -->
      
    <!-- 支付 -->
    <div class="detail-action van-hairline--top">
      <div class="price">
        <span>需付款</span>
        <span>¥39.00</span>
      </div>
      <van-button type="default" round>取消问诊</van-button>
      <van-button type="primary" round>继续支付</van-button>
    </div>
  </div>
</template>

2、样式

less
.consult-detail-page {
  padding: 46px 0 110px 0;
}
.detail-head {
  height: 140px;
  position: relative;
  &::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 135px;
    background: linear-gradient(180deg, rgba(44, 181, 165, 0), rgba(44, 181, 165, 0.2));
    border-bottom-left-radius: 150px 20px;
    border-bottom-right-radius: 150px 20px;
  }
  padding: 15px;
  .text {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    padding: 10px 3px;
    .sta {
      color: var(--cp-tag);
      font-weight: 500;
      font-size: 16px;
      &.green {
        color: var(--cp-primary);
      }
      &.orange {
        color: #f2994a;
      }
    }
    .tip {
      width: 100%;
      color: var(--cp-text3);
      margin-top: 5px;
    }
  }
  .card {
    height: 74px;
    background-color: #fff;
    border-radius: 8px;
    position: relative;
    display: flex;
    align-items: center;
    padding: 0 15px;
    box-shadow: 0px 0px 22px 0px rgba(229, 229, 229, 0.5);
    .avatar {
      width: 38px;
      height: 38px;
    }
    .doc {
      flex: 1;
      padding-left: 15px;
      > span {
        display: block;
        font-size: 16px;
        &:last-child {
          font-size: 13px;
          color: var(--cp-text3);
        }
      }
    }
    .van-icon {
      color: var(--cp-tip);
    }
  }
}
.detail-patient {
  &::after {
    content: '';
    display: block;
    height: 12px;
    background-color: var(--cp-bg);
  }
}
.detail-order {
  > h3 {
    padding: 10px 18px;
    font-weight: normal;
  }
  .copy {
    padding: 2px 10px;
    border: 1px solid var(--cp-primary);
    background-color: var(--cp-plain);
    color: var(--cp-primary);
    font-size: 12px;
    border-radius: 12px;
    margin-right: 10px;
  }
  :deep(.van-cell__title) {
    width: 70px;
    flex: none;
  }
  .price :deep(.van-cell__value) {
    font-size: 16px;
    color: var(--cp-price);
  }
}
.detail-action {
  height: 65px;
  width: 100%;
  position: fixed;
  left: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  background-color: #fff;
  justify-content: flex-end;
  padding: 0 15px;
  box-sizing: border-box;
  .price {
    flex: 1;
    > span:last-child {
      font-size: 18px;
      color: var(--cp-price);
      padding-left: 5px;
    }
  }
  .van-button {
    margin-left: 10px;
    padding-left: 17px;
    padding-right: 17px;
  }
  :deep(.van-button--default) {
    background-color: var(--cp-bg);
    color: var(--cp-text3);
  }
}
.van-cell {
  padding-left: 18px;
  padding-right: 18px;
}
.detail-time {
  position: fixed;
  left: 0;
  bottom: 65px;
  width: 100%;
  height: 44px;
  background-color: #fff7eb;
  text-align: center;
  line-height: 44px;
  font-size: 13px;
  color: #f2994a;
  .van-count-down {
    display: inline;
    color: #f2994a;
  }
}

骨架屏

html
  <div class="consult-detail-page" v-if="item">
  // ...
  </div>
  <div class="consult-detail-page" v-else>
    <cp-nav-bar title="问诊详情" />
    <van-skeleton title :row="4" style="margin-top: 30px" />
    <van-skeleton title :row="4" style="margin-top: 30px" />
  </div>

请求数据

1、在 consult-detail 组件中,调用 getConsultOrderDetail() 请求数据

image-20250407221130014

image-20250407221202626

2、渲染页面

image-20250407221957772

3、封装患病时长、就诊情况的文本映射hooks

image-20250407222301833

4、导入hooks并使用

image-20250407222446084

封装:consult-more

image-20250407225309320

页面布局

1、使用 consult-more 组件

image-20250407225225730

2、HTML

image-20250407223728426

3、popover逻辑

image-20250407225109179

禁用功能

1、在使用组件处传入disabled属性

image-20250407225829353

2、在组件中接收disabled属性,并通过它决定气泡项是否禁用

image-20250407225519203

删除订单/查看处方

1、在组件中向外暴露 on-delete 和 on-preview事件

image-20250407225747202

2、在父组件中监听暴露的事件,实现删除订单、查看处方

image-20250407225917883

倒计时

image-20250407230255673

操作按钮

image-20250407230136093

不同status,对应不同的按钮

image-20250407230641372

image-20250407230726821

image-20250407230823662

image-20250407231012654

image-20250407231126023

封装-取消订单

1、封装hooks

image-20250408105941609

2、在 consult-order-item 组件中使用hook

image-20250408110129131

3、在 consult-detail 组件中使用hook

image-20250408110129131

image-20250408110319844

image-20250408110407734

封装-删除订单

1、封装hooks

image-20250408110813745

2、在 consult-order-item 组件中使用hook,并向外暴露 on-delete 事件

image-20250408111117065

3、在 consult-detail 组件中使用hook,并跳转到 consult-order 页面

image-20250408111336702

image-20250408111426470

image-20250408111514313

4、查看处方

image-20250408111627431

image-20250408111718783

image-20250408111813517

复制订单号@

1、绑定点击事件 onCopy 到 复制 处

image-20250408114222426

2、在 onCopy 中实现复制订单号到剪切板

image-20250408114354128

封装-支付抽屉

1、封装 cp-pay-sheet 支付抽屉全局组件

image-20250408120643669

image-20250408115807841

2、在 consult-pay 组件中使用 cp-pay-sheet 组件,并传入属性

image-20250408115450109

3、在 consult-detail 组件中使用 cp-pay-sheet 组件,并传入属性

image-20250408120127215

image-20250408120314428

Room

image-20250316210821620

image-20250316210709143

路由规则

image-20250313150703607

页面布局

问诊室

html
<script setup lang="ts">
import RoomStatus from './components/RoomStatus.vue'
import RoomAction from './components/RoomAction.vue'
import RoomMessage from './components/RoomMessage.vue'
</script>

<template>
  <div class="room-page">
    <cp-nav-bar title="问诊室" />
    <room-status></room-status>
    <room-message></room-message>
    <room-action></room-action>
  </div>
</template>

状态栏

image-20250316211535084

html
<template>
  <div class="room-status">
    <!-- 待接诊 -->
    <div class="wait">已通知医生尽快接诊,24小时内医生未回复将自动退款</div>
    <!-- 咨询中 -->
    <div class="chat">
      <span>咨询中</span>
      <span>剩余时间:23:10:34</span>
    </div>
    <!-- 已结束 -->
    <div class="end"><van-icon name="passed" />已结束</div>
  </div>
</template>

操作栏

image-20250316211813158

注意:操作栏默认处于禁用状态

html
<template>
  <div class="room-action">
    <van-field
      type="text"
      class="input"
      :border="false"
      placeholder="问医生"
      autocomplete="off"
      :disabled="true"
    ></van-field>
    <van-uploader :preview-image="false" :disabled="true">
      <cp-icon name="consult-img" />
    </van-uploader>
  </div>
</template>

消息卡片

html
<template>
  <!-- 患者卡片 -->
  <div class="msg msg-illness">
    <div class="patient van-hairline--bottom">
      <p>李富贵 男 31岁</p>
      <p>一周内 | 未去医院就诊</p>
    </div>
    <van-row>
      <van-col span="6">病情描述</van-col>
      <van-col span="18">头痛、头晕、恶心</van-col>
      <van-col span="6">图片</van-col>
      <van-col span="18">点击查看</van-col>
    </van-row>
  </div>
  <!-- 通知-通用 -->
  <div class="msg msg-tip">
    <div class="content">
      <span>医护人员正在赶来,请耐心等候</span>
    </div>
  </div>
  <!-- 通知-温馨提示 -->
  <div class="msg msg-tip">
    <div class="content">
      <span class="green">温馨提示:</span>
      <span>在线咨询不能代替面诊,医护人员建议仅供参考</span>
    </div>
  </div>
  <!-- 通知-结束 -->
  <div class="msg msg-tip msg-tip-cancel">
    <div class="content">
      <span>订单取消</span>
    </div>
  </div>
  <!-- 发送文字 -->
    <div class="msg msg-to">
    <div class="content">
      <div class="time">20:12</div>
      <div class="pao">大夫你好?</div>
    </div>
    <van-image src="https://yjy-oss-files.oss-cn-zhangjiakou.aliyuncs.com/tuxian/popular_3.jpg" />
  </div>
  <!-- 发送图片 -->
  <div class="msg msg-to">
    <div class="content">
      <div class="time">20:12</div>
      <van-image 
        fit="contain" 
        src="https://yjy-oss-files.oss-cn-zhangjiakou.aliyuncs.com/tuxian/popular_3.jpg"
      />
    </div>
    <van-image src="https://yjy-oss-files.oss-cn-zhangjiakou.aliyuncs.com/tuxian/popular_3.jpg" />
  </div>
  <!-- 接收文字 -->
  <div class="msg msg-from">
    <van-image src="https://yjy-oss-files.oss-cn-zhangjiakou.aliyuncs.com/tuxian/popular_3.jpg" />
    <div class="content">
      <div class="time">20:12</div>
      <div class="pao">哪里不舒服</div>
    </div>
  </div>
  <!-- 接收图片 -->
  <div class="msg msg-from">
    <van-image src="https://yjy-oss-files.oss-cn-zhangjiakou.aliyuncs.com/tuxian/popular_3.jpg" />
    <div class="content">
      <div class="time">20:12</div>
      <van-image 
        fit="contain" 
        src="https://yjy-oss-files.oss-cn-zhangjiakou.aliyuncs.com/tuxian/popular_3.jpg"
      />
    </div>
  </div>
  <!-- 处方卡片 -->
  <div class="msg msg-recipe">
    <div class="content">
      <div class="head van-hairline--bottom">
        <div class="head-tit">
          <h3>电子处方</h3>
          <p>原始处方 <van-icon name="arrow"></van-icon></p>
        </div>
        <p>李富贵 男 31岁 血管性头痛</p>
        <p>开方时间:2022-01-15 14:21:42</p>
      </div>
      <div class="body">
        <div class="body-item" v-for="i in 2" :key="i">
          <div class="durg">
            <p>优赛明 维生素E乳</p>
            <p>口服,每次1袋,每天3次,用药3天</p>
          </div>
          <div class="num">x1</div>
        </div>
      </div>
      <div class="foot"><span>购买药品</span></div>
    </div>
  </div>
  <!-- 评价卡片,后期实现 -->
</template>

<style lang="scss" scoped>
@import '@/styles/room.scss';
</style>

TS类型

1、枚举类型

ts
// 消息类型
export enum MsgType {
  /** 文字聊天 */
  MsgText = 1,
  /** 消息聊天 */
  MsgImage = 4,
  /** 患者信息 */
  CardPat = 21,
  /** 处方信息 */
  CardPre = 22,
  /** 未评价信息 */
  CardEvaForm = 23,
  /** 已评价信息 */
  CardEva = 24,
  /** 通用通知 */
  Notify = 31,
  /** 温馨提示 */
  NotifyTip = 32,
  /** 取消提示 */
  NotifyCancel = 33
}

// 处方状态
export enum PrescriptionStatus {
  /** 未付款 */
  NotPayment = 1,
  /** 已付款 */
  Payment = 2,
  /** 已失效 */
  Invalid = 3
}

2、问诊室类型

ts
import { MsgType, PrescriptionStatus } from '@/enums'
import type { Consult, Image } from './consult'
import type { Patient } from './user'

export type Medical = {
  /** 药品ID */
  id: string
  /** 药品名称 */
  name: string
  /** 金额 */
  amount: string
  /** 药品图片 */
  avatar: string
  /** 规格信息 */
  specs: string
  /** 用法用量 */
  usageDosag: string
  /** 数量 */
  quantity: string
  /** 是否处方,0 不是 1 是 */
  prescriptionFlag: 0 | 1
}

export type Prescription = {
  /** 处方ID */
  id: string
  /** 药品订单ID */
  orderId: string
  /** 创建时间 */
  createTime: string
  /** 患者名称 */
  name: string
  /** 问诊记录ID */
  recordId: string
  /** 性别 0 女 1 男 */
  gender: 0 | 1
  /** 性别文字 */
  genderValue: ''
  /** 年龄 */
  age: number
  /** 诊断信息 */
  diagnosis: string
  /** 处方状态 */
  status: PrescriptionStatus
  /** 药品清单 */
  medicines: Medical[]
}

export type EvaluateDoc = {
  /** 评价ID */
  id?: string
  /** 评分 */
  score?: number
  /** 内容 */
  content?: string
  /** 创建时间 */
  createTime?: string
  /** 创建人 */
  creator?: string
}

export type Message = {
  /** 消息ID */
  id: string
  /** 消息类型 */
  msgType: MsgType
  /** 发信人 */
  from?: string
  /** 发信人ID */
  fromAvatar?: string
  /** 收信人 */
  to?: string
  /** 收信人头像 */
  toAvatar?: string
  /** 创建时间 */
  createTime: string
  /** 消息主体 */
  msg: {
    /** 文本内容 */
    content?: string
    /** 图片对象 */
    picture?: Image
    /** 问诊记录,患者信息 */
    consultRecord?: Consult & {
      patientInfo: Patient
    }
    /** 处方信息 */
    prescription?: Prescription
    /** 评价信息 */
    evaluateDoc?: EvaluateDoc
  }
}

// 消息分组列表
export type TimeMessages = {
  /** 分组消息最早时间 */
  createTime: string
  /** 消息数组 */
  items: Message[]
  /** 订单ID */
  orderId: string
  /** 会话ID */
  sid: string
}

WebSocket

接口

image-20250322140220665

建立连接

image-20250322134955632

1、安装socket.io-client

sh
pnpm i socket.io-client

2、在 onMounted/onUnmounted 钩子中,建立/关闭socket连接

image-20250322135946894

聊天记录

获取数据

在 onMounted 钩子中,监听 chatMsgList,获取聊天记录。

image-20250322142832582

渲染聊天记录

1、在room组件中,传入获取到的聊天记录数据,并遍历传递到room-message组件中

image-20250322143132154

2、在room-message组件中,渲染聊天记录消息

2.1、抽取常量数据

image-20250322144250188

2.2、根据常量数据转换聊天记录数据类型

image-20250322144317155

2.3、渲染页面

image-20250322144419872

image-20250322144425328

3、预览图片

image-20250322144533179

image-20250322144854827

渲染通用通知

image-20250322152017626

image-20250322152153570

渲染温馨提示

image-20250322152105391

image-20250322152201909

接诊状态

image-20250322152358176

TS类型

1、问诊订单状态枚举类型

ts
export enum OrderType {
  // 问诊订单
  /** 待支付 */
  ConsultPay = 1,
  /** 待接诊 */
  ConsultWait = 2,
  /** 问诊中 */
  ConsultChat = 3,
  /** 问诊完成 */
  ConsultComplete = 4,
  /** 取消问诊 */
  ConsultCancel = 5,
    
  // 药品订单
  /** 待支付 */
  MedicinePay = 10,
  /** 待发货 */
  MedicineSend = 11,
  /** 待收货 */
  MedicineTake = 12,
  /** 已完成 */
  MedicineComplete = 13,
  /** 取消订单 */
  MedicineCancel = 14
}

2、问诊订单TS类型

ts
// 问诊订单单项信息
export type ConsultOrderItem = Consult & {
  /** 创建时间 */
  createTime: string
  /** 医生信息 */
  docInfo?: Doctor
  /** 患者信息 */
  patientInfo: Patient
  /** 订单编号 */
  orderNo: string
  /** 订单状态 */
  status: OrderType
  /** 状态文字 */
  statusValue: string
  /** 类型问诊文字 */
  typeValue: string
  /** 倒计时时间 */
  countdown: number
  /** 处方ID */
  prescriptionId?: string
  /** 评价ID */
  evaluateId: number
  /** 应付款 */
  payment: number
  /** 优惠券抵扣 */
  couponDeduction: number
  /** 积分抵扣 */
  pointDeduction: number
  /** 实付款 */
  actualPayment: number
}

接口

  • URL/patient/consult/order/detail

  • 类型GET

  • token:携带

  • 参数

    ts
    {
      orderId: string // 订单id
    }
  • 返回数据:ConsultOrderItem

请求数据

1、在 services/room 中发送网络请求

image-20250322153532976

2、在 room 组件的 onMounted 钩子中,调用请求方法,获取数据

image-20250322153941794

3、在 room 组件的订单状态变化时,重新获取数据

image-20250322154147866

控制组件

控制状态栏

1、传递订单详情数据给 room-status 组件

image-20250322155005860

2、在 room-status 组件中接收传递的数据

image-20250322155026023

3、根据传递的数据渲染不同的状态

image-20250322155121939

控制操作栏

1、根据获取的订单详情数据,传递disabled值给 room-action 组件

image-20250322155501775

2、在 room-action 组件中接收 disabled 属性

image-20250322155525015

3、根据 disabled 属性,控制操作栏是否可用

image-20250322155549208

文字聊天

image-20250322155710130

发送文字

1、在 room-action 组件中,监听 enter 键点击事件,发送 send-text 事件到父组件

image-20250322160626379

2、在 room 组件中,监听 send-text 事件,实现socket发送文字消息

image-20250322160735357

渲染文字

image-20250322160841021

1、在 room 组件中通过socket接收聊天消息

image-20250322161414067

2、在 room-message 组件中,渲染接收的聊天消息

image-20250322161503245

image-20250322161525306

3、接收消息后,在nextTick()后,滚动页面到底部

image-20250322161758251

4、修改时间格式

4.1、安装 dayjs 插件:pnpm i dayjs

4.2、定义格式化时间方法

image-20250322162104267

4.3、调用方法

image-20250322162126811

image-20250322162128877

图片聊天

image-20250322162219829

1、上传图片:在 room-action 组件中,绑定 sendImage() 方法到 after-read 属性上

image-20250322162942582

2、在 sendImage() 方法中,调用 uploadImage() 方法发送网络请求,上传图片,并向外发射 send-image 事件

image-20250322163124597

image-20250322163139403

3、发送图片socket:在 room 组件中,绑定onSendImage() 方法到 send-image 事件

image-20250322163526775

4、在 onSendImage() 方法中,通过socket发送图片消息

image-20250322163603666

5、渲染消息

image-20250322163940716

image-20250322163955850

聊天记录

image-20250322164251553

默认显示最新消息

image-20250322164946377

下拉刷新获取聊天记录

1、使用 van-pull-refresh 组件包裹 room-message,实现下拉刷新

image-20250322165955205

2、在下拉刷新处理函数 onRefresh() 中,通过socket获取历史聊天记录

image-20250322170300302

3、记录每一段消息中最早的消息时间,并在加载完毕后设置loading为false,并在没有聊天记录数据时提示

image-20250322170208687

消息已读

image-20250325155907719

接口-所有未读消息数量

  • URL/patient/message/unRead/all

  • 类型GET

  • token:携带

  • 参数:无

  • 返回数据

    image-20250325160104050

渲染数据

1、在 services/room.js 中发送网络请求

image-20250325160640242

2、在 layout 组件的 onMounted 中调用请求方法,获取数据

image-20250325160834466

3、渲染数据到 layout 组件的消息通知项中

image-20250325160959580

实现消息已读

1、在room 组件的 onMounted 中,在第一次进入聊天室时,设置消息已读

image-20250325161714245

2、在聊天时,每收到一条消息就将其设置为已读

image-20250325161819794

查看处方

image-20250325163732679

渲染处方卡片

image-20250325162524643

接口-获取处方图片

  • URL/patient/consult/prescription/:id

  • 类型GET

  • token:携带

  • 参数id: string // 处方id

  • 返回数据

    image-20250325163029539

渲染数据

1、在 services/room.js 中发送网络请求,获取处方图片地址

image-20250325163316754

2、在 room-message 组件中,绑定点击事件 onShowPrescription()

image-20250325163456506

3、在onShowPrescription()中,实现预览处方图片

image-20250325163815550

评价医生:evaluate-card

image-20250325164047558

展示结束问诊消息

image-20250325164250436

展示评价卡片

1、HTML

html
<template>
  <!-- 已评价 -->
  <div class="evaluate-card">
    <p class="title">医生服务评价</p>
    <p class="desc">我们会更加努力提升服务质量</p>
    <van-rate
      :modelValue="3"
      size="7vw"
      gutter="3vw"
      color="#FADB14"
      void-icon="star"
      void-color="rgba(0,0,0,0.04)"
    />
  </div>
    
  <!-- 未评价 -->
  <div class="evaluate-card">
    <p class="title">感谢您的评价</p>
    <p class="desc">本次在线问诊服务您还满意吗?</p>
    <van-rate
      size="7vw"
      gutter="3vw"
      color="#FADB14"
      void-icon="star"
      void-color="rgba(0,0,0,0.04)"
    />
    <van-field
      type="textarea"
      maxlength="150"
      show-word-limit
      rows="3"
      placeholder="请描述您对医生的评价或是在医生看诊过程中遇到的问题"
    ></van-field>
    <div class="footer">
      <van-checkbox>匿名评价</van-checkbox>
      <van-button type="primary" size="small" round> 提交评价 </van-button>
    </div>
  </div>
</template>

2、样式

css
.evaluate-card {
  width: 100%;
  background-color: #fff;
  border-radius: 8px;
  overflow: hidden;
  text-align: center;
  padding: 15px;
  .title {
    font-size: 15px;
    margin-bottom: 5px;
  }
  .desc {
    font-size: 12px;
    margin-bottom: 15px;
    color: var(--cp-tip);
  }
  .van-field {
    background-color: var(--cp-bg);
    margin: 15px 0;
    border-radius: 8px;
  }
  .footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    box-sizing: border-box;
    :deep() {
      .van-checkbox {
        .van-icon {
          font-size: 12px;
        }
        &__label {
          font-size: 12px;
          color: var(--cp-tip);
        }
        height: 16px;
      }
      .van-button {
        padding: 0 16px;
        &.disabled {
          opacity: 1;
          background: #fafafa;
          color: #d9dbde;
          border: #fafafa;
        }
      }
    }
  }
}

渲染数据

1、使用评价卡片组件

image-20250325165016271

2、在评价卡片组件中,接收传递的数据

image-20250325165225706

3、根据 evaluateDoc 是否有数据,展示不同的内容

image-20250325165318075

收集评价信息

1、定义评价数据

image-20250325165953746

2、绑定数据到表单中

image-20250325165850031

3、绑定提交按钮点击方法 onSubmit,并在其中提交评价信息

image-20250325170118216

4、提交评价前校验

image-20250325170209228

接口-评价医生

  • URL/patient/order/evaluate

  • 类型POST

  • token:携带

  • 参数

    ts
    {
      docId: string // 评价的医生id
      orderId: string // 订单id
      score: number // 分数
      content: string  // 评价内容
      anonymousFlag: 0 | 1 // 匿名标志,0:不是匿名,1:是匿名
    }
  • 返回数据

    image-20250325170722146

提交评价

image-20250325170900104

1、在 services/room.js 中,发送网络请求

image-20250325171130084

2、docIdorderId需要由祖父组件room的consult传递

image-20250325171355405

3、在后代组件 evaluate-card 中,使用 inject() 注入provide() 提供的响应式数据

image-20250325171710039

4、在 evaluate-card 组件的onSubmit中,调用请求方法实现评价医生

image-20250325172209865

修改父组件中的数据

1、在祖父组件 room 中,定义completeEva()方法找打并修改评价消息数据,并将其提供给子孙组件

image-20250325173052988

2、在子孙组件 evaluate-card 中,注入祖父组件提供的completeEva()方法,并调用它

image-20250325172930136

3、渲染已评价卡片

image-20250325173209716

Order

image-20250408120955145

页面布局

页面入口

1、在 room 组件的处方卡片上绑定 buy 处理方法

image-20250408121207261

image-20250408121802283

药品支付:order-medicine-pay

image-20250408122135718

页面布局

1、HTML

html
<script setup lang="ts"></script>

<template>
  <div class="order-pay-page">
    <cp-nav-bar title="药品支付" />
    <div class="order-address">
      <p class="area">
        <van-icon name="location" />
        <span>北京市昌平区</span>
      </p>
      <p class="detail">建材城西路金燕龙办公楼999号</p>
      <p>李富贵 13211112222</p>
    </div>
    <div class="order-medical">
      <div class="head">
        <h3>优医药房</h3>
        <small>优医质保 假一赔十</small>
      </div>
      <div class="item van-hairline--top" v-for="i in 2" :key="i">
        <img class="img" src="@/assets/ad.png" alt="" />
        <div class="info">
          <p class="name">
            <span>优赛明 维生素E乳</span>
            <span>x1</span>
          </p>
          <p class="size">
            <van-tag>处方药</van-tag>
            <span>80ml</span>
          </p>
          <p class="price">¥25.00</p>
        </div>
        <div class="desc">用法用量:口服,每次1袋,每天3次,用药3天</div>
      </div>
    </div>
    <div class="order-detail">
      <van-cell-group>
        <van-cell title="药品金额" value="¥50" />
        <van-cell title="运费" value="¥4" />
        <van-cell title="优惠券" value="-¥0" />
        <van-cell title="实付款" value="¥54" class="price" />
      </van-cell-group>
    </div>
    <div class="order-tip">
      <p class="tip">
        由于药品的特殊性,如非错发、漏发药品的情况,药品一经发出
        不得退换,请核对药品信息无误后下单。
      </p>
      <van-checkbox>我已同意<a href="javascript:;">支付协议</a></van-checkbox>
    </div>
    <van-submit-bar
      :price="50 * 100"
      button-text="立即支付"
      button-type="primary"
      text-align="left"
    ></van-submit-bar>
  </div>
</template>

2、样式

less
:deep(.van-nav-bar) {
  background-color: var(--cp-primary);
  .van-nav-bar__arrow,
  .van-nav-bar__title {
    color: #fff;
  }
}
:deep(.van-cell) {
  .van-cell__title {
    font-size: 16px;
  }
  .van-cell__value {
    font-size: 16px;
  }
  &.price {
    .van-cell__value {
      font-size: 18px;
      color: var(--cp-price);
    }
  }
}
:deep(.van-submit-bar) {
  box-shadow: 0 0 2px rgba(0, 0, 0, 0.1);
  .van-button {
    width: 200px;
  }
}
.order-pay-page {
  padding: 46px 0 65px;
}
.order-address {
  padding: 15px 15px 0 15px;
  background-color: #fff;
  font-size: 13px;
  .area {
    color: var(--cp-tag);
    margin-bottom: 5px;
    .van-icon-location {
      color: #ff7702;
      font-size: 14px;
    }
  }
  .detail {
    font-size: 17px;
    margin-bottom: 5px;
  }
  &::after {
    content: '';
    display: block;
    height: 12px;
    background-color: var(--cp-bg);
    margin: 0 -15px;
    margin-top: 15px;
  }
}

.order-medical {
  background-color: #fff;
  padding: 0 15px;
  .head {
    display: flex;
    height: 54px;
    align-items: center;
    > h3 {
      font-size: 16px;
      font-weight: normal;
    }
    > small {
      font-size: 13px;
      color: var(--cp-tag);
      margin-left: 10px;
    }
  }
  .item {
    display: flex;
    flex-wrap: wrap;
    padding: 15px 0;
    .img {
      width: 80px;
      height: 70px;
      border-radius: 2px;
      overflow: hidden;
    }
    .info {
      padding-left: 15px;
      width: 250px;
      .name {
        display: flex;
        font-size: 15px;
        margin-bottom: 5px;
        > span:first-child {
          width: 200px;
        }
        > span:last-child {
          width: 50px;
          text-align: right;
        }
      }
      .size {
        margin-bottom: 5px;
        .van-tag {
          background-color: var(--cp-primary);
          vertical-align: middle;
        }
        span:not(.van-tag) {
          margin-left: 10px;
          color: var(--cp-tag);
          vertical-align: middle;
        }
      }
      .price {
        font-size: 16px;
        color: #eb5757;
      }
    }
    .desc {
      width: 100%;
      background-color: var(--cp-bg);
      border-radius: 4px;
      margin-top: 10px;
      padding: 4px 10px;
      color: var(--cp-tip);
    }
  }
}
.order-tip {
  padding: 0 15px;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  margin-top: 10px;
  .tip {
    font-size: 12px;
    color: var(--cp-tag);
    width: 100%;
    &::before {
      content: '*';
      color: var(--cp-price);
      font-size: 14px;
    }
    margin-bottom: 30px;
  }
  .van-checkbox {
    a {
      color: var(--cp-primary);
    }
  }
}

路由规则

image-20250408122300432

渲染数据

接口-药品预支付
  • URL/patient/medicine/order/pre

  • 类型GET

  • token:携带

  • 参数

    ts
    {
      prescriptionId: string // 处方id
      couponId?: string // 优惠券id
      useCoupon?: string // 是否使用优惠券,1:使用,0:不使用,默认:1
    }
  • 返回数据

    image-20250408142817281

  • TS类型

    ts
    /** 药品预支付 */
    export type OrderPre = {
      /** 处方ID */ id: string
      /** 优惠券ID */ couponId: string
      /** 积分抵扣 */ pointDeduction: number
      /** 优惠券抵扣 */ couponDeduction: number
      /** 应付款 */ payment: number
      /** 邮费 */ expressFee: number
      /** 实付款 */ actualPayment: number
      /** 药品订单 */ medicines: Medical[]
    }
接口-收货地址
  • URL/patient/order/address

  • 类型GET

  • token:携带

  • 参数:NULL

  • 返回数据

    image-20250408144004881

  • TS类型

    ts
    export type AddressItem = {
      /** 地址ID */ id: string
      /** 联系方式 */ mobile: string
      /** 收件人 */ receiver: string
      /** 省 */ province: string
      /** 市 */ city: string
      /** 区 */ county: string
      /** 详细地址 */ addressDetail: string
      /** 是否默认地址,0 不是 1 是 */ isDefault: 0 | 1
    }
网络请求

1、在 services/order.ts 中发送网络请求 getMedicalOrderPre(),此处只需要传入参数prescriptionId

image-20250408143820776

2、在 services/order.ts 中发送网络请求 getAddressList()

image-20250408144052616

组件渲染

1、在 order-medicine-pay 组件中调用网络请求方法获取 预支付信息 数据

image-20250408160746536

2、在 order-medicine-pay 组件中调用网络请求方法获取 收货地址 数据

image-20250408161055486

3、在 onMounted 钩子中调用

image-20250408161118811

image-20250408161223382

4、骨架屏

image-20250408162400156

5、渲染获取的数据

image-20250408162139527

image-20250408162306641

完成支付

image-20250408162728868

接口-生成药品订单ID
  • URL/patient/medicine/order

  • 类型POST

  • token:携带

  • 参数

    ts
    {
      id: string // 处方ID
      couponId?: string // 优惠券ID
      addressId: string // 地址信息ID
    }
  • 返回数据

    image-20250408163030824

  • TS类型

    ts
网络请求

在services/order.ts 中发送网络请求 getMedicalOrderId()

image-20250408163234908

生成药品订单ID

1、在 order-medicine-pay 组件中绑定立即支付处理函数 onSubmit

image-20250408163454461

2、在 onSubmit 中调用网络请求方法实现支付

image-20250408164254755

重构:cp-pay-sheet

1、重构 cp-pay-sheet 支付抽屉组件,向外暴露 payCallback 跳转地址

image-20250408164706382

2、在 order-medicine-pay 组件中使用 cp-pay-sheet 支付抽屉组件

image-20250408165339775

image-20250408165429272

检测受影响位置@

如果想知道在组件中修改了props之后,哪些位置会受到影响,可以通过以下方法:

sh
# 在命令行中输入:
pnpm type-check

image-20250408165017010

支付结果:order-medicine-pay-result

image-20250408171321827

页面布局

1、HTML

html
<template>
  <div class="order-pay-result-page">
    <cp-nav-bar title="药品支付结果" />
    
    <!-- 支付失败 -->
    <div class="result">
      <van-icon name="clear" />
      <p class="price">¥ 100.00</p>
      <p class="status">支付失败</p>
      <p class="tip">
        订单支付失败,可以点击查看订单继续支付,如有疑问联系客服~
      </p>
    </div>
      
    <!-- 支付成功 -->
    <div class="result">
      <van-icon name="checked" />
      <p class="price">¥ 100.00</p>
      <p class="status">支付成功</p>
      <p class="tip">订单支付成功,已通知药房尽快发出, 请耐心等待~</p>
    </div>
    <div class="btn">
      <van-button type="primary" :to="`/order/10000`">查看订单</van-button>
      <van-button :to="`/room?orderId=10000`">返回诊室</van-button>
    </div>
  </div>
</template>

2、样式

less
.order-pay-result-page {
  padding-top: 46px;
  .result {
    display: flex;
    flex-direction: column;
    align-items: center;
    .van-icon {
      font-size: 75px;
      margin-top: 60px;
    }
    .van-icon-clear {
      color: var(--cp-price);
    }
    .van-icon-checked {
      color: var(--cp-primary);
    }
    .price {
      font-size: 22px;
      margin-top: 10px;
    }
    .status {
      color: var(--cp-text3);
    }
    .tip {
      color: var(--cp-tip);
      width: 240px;
      text-align: center;
      margin-top: 20px;
    }
  }
  .btn {
    margin-top: 60px;
    display: flex;
    justify-content: center;
    .van-button--primary {
      margin-right: 20px;
    }
  }
}

路由规则

image-20250408165907417

渲染数据

接口-药品订单信息
  • URL/patient/medicine/order/detail/${id}

  • 类型GET

  • token:携带

  • 参数

    ts
    {
      id: string // 药品订单ID
    }
  • 返回数据

    image-20250408170440777

  • TS类型

    ts
    type Address = Omit<AddressItem, 'isDefault'>
    
    export type OrderDetail = {
      /** 药品订单ID */ id: string
      /** 药品订单编号 */ orderNo: string
      /** 订单类型 */ type: number
      /** 创建时间 */ createTime: string
      /** 处方ID */ prescriptionId: string
      /** 订单状态 */ status: OrderType
      /** 订单状态说明 */ statusValue: string // 10待支付,11待发货,12待收货,13已完成,14已取消
      /** 药品清单 */ medicines: Medical[]
      /** 支付倒计时时间 */ countDown: number
      /** 收货地址 */ addressInfo: Address
      /** 物流信息 */ expressInfo: {
        /** 物流最新位置 */ content: string
        /** 物流最新时间 */ time: string
      }
      /** 支付时间 */ payTime: string
      /** 支付方式 */ paymentMethod?: 0 | 1
      /** 支付金额 */ payment: number
      /** 积分抵扣 */ pointDeduction: number
      /** 优惠券抵扣 */ couponDeduction: number
      /** 邮费 */ expressFee: number
      /** 实付金额 */ actualPayment: number
      /** 问诊室ID */ roomId: string
    }
网络请求

在 services/order.ts 中发送网络请求

image-20250408171111764

组件渲染

1、在 order-medicine-pay-result 组件中获取数据

image-20250408172012920

image-20250408171415770

2、渲染获取的数据

image-20250408172328689

订单详情:order-medicine-detail

image-20250408172434939

页面布局

1、HTML

html
<template>
  <div class="order-detail-page">
    <cp-nav-bar title="药品订单详情" />
    <div class="order-head">
      <!-- <div class="address">
        <p class="area">
          <van-icon name="location" />
          <span>北京市昌平区</span>
        </p>
        <p class="detail">建材城西路金燕龙办公楼999号</p>
        <p>李富贵 13211112222</p>
      </div> -->
      <div class="card">
        <div class="logistics">
          <p>【东莞市】您的包裹已由物流公司揽收</p>
          <p>2019-07-14 17:42:12</p>
        </div>
        <van-icon name="arrow" />
      </div>
    </div>
    <div class="order-medical">
      <div class="head">
        <h3>优医药房</h3>
        <small>优医质保 假一赔十</small>
      </div>
      <div class="item van-hairline--top" v-for="i in 2" :key="i">
        <img class="img" src="@/assets/ad.png" alt="" />
        <div class="info">
          <p class="name">
            <span>优赛明 维生素E乳</span>
            <span>x1</span>
          </p>
          <p class="size">
            <van-tag>处方药</van-tag>
            <span>80ml</span>
          </p>
          <p class="price">¥25.00</p>
        </div>
        <div class="desc">用法用量:口服,每次1袋,每天3次,用药3天</div>
      </div>
    </div>
    <div class="order-detail">
      <van-cell-group>
        <van-cell title="药品金额" value="¥50.00" />
        <van-cell title="运费" value="¥4.00" />
        <van-cell title="优惠券" value="-¥0.00" />
        <van-cell title="实付款" value="¥54.00" class="price" />
        <van-cell title="订单编号" value="202201127465" />
        <van-cell title="创建时间" value="2022-01-23 09:23:46" />
        <van-cell title="支付时间" value="2022-01-23 09:23:46" />
        <van-cell title="支付方式" value="支付宝支付" />
      </van-cell-group>
    </div>
    <!-- 已取消 -->
    <!-- <van-action-bar>
      <van-action-bar-icon icon="delete-o" text="删除" />
      <van-action-bar-button type="primary" text="沟通记录" />
    </van-action-bar> -->
    <!-- 待收货 -->
    <van-action-bar>
      <van-action-bar-button type="primary" text="确认收货" />
    </van-action-bar>
    <!-- 待发货 -->
    <!-- <van-action-bar>
      <van-action-bar-button type="primary" text="提醒发货" />
    </van-action-bar> -->
    <!-- 待支付 -->
    <!-- <van-action-bar>
      <p class="price">需要支付:<span>¥60</span></p>
      <van-action-bar-button color="#bbb" text="取消订单" />
      <van-action-bar-button type="primary" text="继续支付" />
    </van-action-bar> -->
    <!-- 已完成 -->
    <!-- <van-action-bar>
      <van-action-bar-icon icon="delete-o" text="删除" />
      <van-action-bar-button type="primary" text="再次购买" />
    </van-action-bar> -->
  </div>
</template>

2、样式

less
.order-detail-page {
  padding-top: 46px;
  padding-bottom: 65px;
}
.address {
  padding: 15px;
  background-color: #fff;
  font-size: 13px;
  position: relative;
  box-shadow: 0px 0px 22px 0px rgba(229, 229, 229, 0.5);
  border-radius: 8px;
  .area {
    color: var(--cp-tag);
    margin-bottom: 5px;
    .van-icon-location {
      color: #ff7702;
      font-size: 14px;
    }
  }
  .detail {
    font-size: 17px;
    margin-bottom: 5px;
  }
}
.order-head {
  position: relative;
  padding: 15px;
  &::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 80px;
    background: linear-gradient(180deg, rgba(44, 181, 165, 0), rgba(44, 181, 165, 0.2));
    border-bottom-left-radius: 150px 20px;
    border-bottom-right-radius: 150px 20px;
  }
  .card {
    height: 74px;
    background-color: #fff;
    border-radius: 8px;
    position: relative;
    display: flex;
    align-items: center;
    padding: 0 15px;
    box-shadow: 0px 0px 22px 0px rgba(229, 229, 229, 0.5);
    .logistics {
      flex: 1;
      p {
        &:first-child {
          color: var(--cp-primary);
        }
        &:last-child {
          color: var(--cp-tag);
          font-size: 13px;
          margin-top: 5px;
        }
      }
    }
    .van-icon {
      color: var(--cp-tip);
    }
  }
}
:deep(.van-cell) {
  .van-cell__title {
    font-size: 14px;
    flex: none;
    width: 100px;
  }
  .van-cell__value {
    font-size: 14px;
  }
  &.price {
    .van-cell__value {
      font-size: 18px;
      color: var(--cp-price);
    }
  }
}
.order-medical {
  background-color: #fff;
  padding: 0 15px;
  .head {
    display: flex;
    height: 54px;
    align-items: center;
    > h3 {
      font-size: 16px;
      font-weight: normal;
    }
    > small {
      font-size: 13px;
      color: var(--cp-tag);
      margin-left: 10px;
    }
  }
  .item {
    display: flex;
    flex-wrap: wrap;
    padding: 15px 0;
    .img {
      width: 80px;
      height: 70px;
      border-radius: 2px;
      overflow: hidden;
    }
    .info {
      padding-left: 15px;
      width: 250px;
      .name {
        display: flex;
        font-size: 15px;
        margin-bottom: 5px;
        > span:first-child {
          width: 200px;
        }
        > span:last-child {
          width: 50px;
          text-align: right;
        }
      }
      .size {
        margin-bottom: 5px;
        .van-tag {
          background-color: var(--cp-primary);
          vertical-align: middle;
        }
        span:not(.van-tag) {
          margin-left: 10px;
          color: var(--cp-tag);
          vertical-align: middle;
        }
      }
      .price {
        font-size: 16px;
        color: #eb5757;
      }
    }
    .desc {
      width: 100%;
      background-color: var(--cp-bg);
      border-radius: 4px;
      margin-top: 10px;
      padding: 4px 10px;
      color: var(--cp-tip);
    }
  }
}
.van-action-bar {
  padding: 0 10px;
  box-shadow: 0 0 2px rgba(0, 0, 0, 0.1);
  .price {
    padding: 0 10px;
    > span {
      font-size: 18px;
      color: var(--cp-price);
    }
  }
}

路由规则

image-20250408172708427

渲染数据

接口-药品订单信息
网络请求

1、封装:获取订单详情的网络请求到hook中

image-20250408173228997

2、在 order-medicine-pay-result 组件中使用 useOrderDetail() 钩子方法

image-20250408173428993

药品清单:order-medicine

1、页面布局

image-20250408173641446

2、在 order-medicine-pay 页面使用组件

image-20250408173820228

组件渲染

1、在 order-medicine-detail 组件中调用 useOrderDetail() 钩子方法获取数据

image-20250408174005939

2、渲染获取的数据

image-20250408175615617

image-20250408175540760

image-20250408175738814

物流详情:order-logistics

image-20250409095606877

页面布局

1、HTML

html
<template>
  <div class="order-logistics-page">
    <!-- 地图 -->
    <div id="map">
      <div class="title">
        <van-icon name="arrow-left" @click="$router.back()" />
        <span>配送中</span>
        <van-icon name="service" />
      </div>
      <div class="current">
        <p class="status">订单派送中 预计明天送达</p>
        <p class="predict">
          <span>申通快递</span>
          <span>7511266366963366</span>
        </p>
      </div>
    </div>
      
    <!-- 物流详情 -->
    <div class="logistics">
      <p class="title">物流详情</p>
      <van-steps direction="vertical" :active="0">
        <van-step>
          <p class="status">派送中</p>
          <p class="content">您的订单正在派送中【深圳市】科技园派送员宋平正在为您派件</p>
          <p class="time">今天天 17:25</p>
        </van-step>
        <van-step v-for="i in 5" :key="i">
          <p class="status">运输中</p>
          <p class="content">在广东深圳公司进行发出扫描</p>
          <p class="time">昨天 10:25</p>
        </van-step>
        <van-step>
          <p class="status">已发货</p>
          <p class="content">卖家已发货</p>
          <p class="time">2022-08-20 10:25</p>
        </van-step>
      </van-steps>
    </div>
  </div>
</template>

2、样式

less
.order-logistics-page {
  --van-step-icon-size: 18px;
  --van-step-circle-size: 10px;
}
#map {
  height: 450px;
  background-color: var(--cp-bg);
  overflow: hidden;
  position: relative;
  .title {
    background-color: #fff;
    height: 46px;
    width: 355px;
    border-radius: 4px;
    display: flex;
    align-items: center;
    padding: 0 15px;
    font-size: 16px;
    position: absolute;
    left: 10px;
    top: 10px;
    box-sizing: border-box;
    box-shadow: 0px 0px 22px 0px rgba(229, 229, 229, 0.5);
    z-index: 999;
    > span {
      flex: 1;
      text-align: center;
    }
    .van-icon {
      font-size: 18px;
      color: #666;
      &:last-child {
        color: var(--cp-primary);
      }
    }
  }
  .current {
    height: 80px;
    border-radius: 4px;
    background-color: #fff;
    height: 80px;
    width: 355px;
    box-sizing: border-box;
    padding: 15px;
    position: absolute;
    left: 10px;
    bottom: 10px;
    box-shadow: 0px 0px 22px 0px rgba(229, 229, 229, 0.5);
    z-index: 999;
    .status {
      font-size: 15px;
      line-height: 26px;
    }
    .predict {
      color: var(--cp-tip);
      font-size: 13px;
      margin-top: 5px;
      > span {
        padding-right: 10px;
      }
    }
  }
}
.logistics {
  padding: 0 10px 20px;
  .title {
    font-size: 16px;
    padding: 15px 5px 5px;
  }
  .van-steps {
    :deep(.van-step) {
      &::after {
        display: none;
      }
    }
    .status {
      font-size: 15px;
      color: var(--cp-text3);
      margin-bottom: 4px;
    }
    .content {
      font-size: 13px;
      color: var(--cp-tip);
      margin-bottom: 4px;
    }
    .time {
      font-size: 13px;
      color: var(--cp-tag);
    }
  }
}

路由规则

image-20250408180748593

渲染数据

接口-物流信息
  • URL/pa }/logistics

  • 类型GET

  • token:携带

  • 参数

    ts
    {
      id: string // 订单ID
    }
  • 返回数据

    image-20250408180614019

  • TS类型

    ts
    export enum ExpressStatus {
      /** 已发货 */ Delivered = 1,
      /** 已揽件 */ Received = 2,
      /** 运输中 */ Transit = 3,
      /** 派送中 */ Delivery = 4,
      /** 已签收 */ Signed = 5
    }
    ts
    export type Express = {
      /** 物流信息ID */ id: string
      /** 物流内容 */ content: string
      /** 创建时间 */ createTime: string
      /** 物流状态 */ status: ExpressStatus
      /** 状态文章 */ statusValue: string
    }
    
    export type Location = {
      /** 经度 */ longitude: string
      /** 纬度 */ latitude: string
    }
    
    export type Logistics = {
      /** 预计送达时间 */ estimatedTime: string
      /** 物流公司名称 */ name: string
      /** 物流编号 */ awbNo: string
      /** 最新物流状态 */ status: ExpressStatus
      /** 最新物流状态文字 */ statusValue: string
      /** 物流信息数组 */ list: Express[]
      /** 轨迹信息数组 */ logisticsInfo: Location[]
      /** 当前运输位置 */ currentLocationInfo: Location
    }
网络请求

在 services/order.ts 中发送网络请求

image-20250409094859341

组件渲染

1、在 order-logistics 组件中调用网络请求方法获取数据

image-20250409095137334

2、渲染获取的数据

image-20250409095534067

集成高德地图

开发文档
参考手册
注册并创建key

1、注册高度地图开放平台账号,并认证成为开发者

2、进入 控制台 -> 应用管理 -> 我的应用 -> 创建新应用

image-20250409102019902

3、进入新建的应用 在线问诊 -> 添加Key

image-20250409102334676

image-20250409102431625

初始化
结合Vue

1、安装loader @amap/amap-jsapi-loader

sh
pnpm add @amap/amap-jsapi-loader

2、在使用高德地图的组件中,配置安全密钥 securityJsCode

注意:2021年12月02日以后申请的key需要配合安全密钥一起使用。

ts
window._AMapSecurityConfig = {
  securityJsCode: '你申请的安全密钥'
}

3、扩展 Window 的类型

ts
// types/global.d.ts

interface Window {
  _AMapSecurityConfig: {
    securityJsCode: string
  }
}

4、在使用高德地图的组件中,初始化时加载高德地图需要的资源

ts
import AMapLoader from '@amap/amap-jsapi-loader'

onMounted(async () => {
  // ... 省略 ...
  AMapLoader.load({
    key: '你申请的key',
    version: '2.0'
  }).then((AMap) => {
    // 使用 Amap 初始化地图
  })
})

5、初始化地图

ts
const map = new AMap.Map('container', {
  mapStyle: 'amap://styles/whitesmoke', // 初始化地图主题样式
  zoom: 11, // 初始化地图缩放级别
  center: [116.397428, 39.90923], // 初始化地图中心点位置
})

image-20250409104707850

插件

JS API 提供了众多的插件,需要引入之后才能使用这些插件的功能。需要通过插件使用的功能有:

服务类地图控件矢量图形编辑工具工具类
PlaceSearch POI 搜索ToolBar 缩放工具条PolylineEditor 折线编辑器MouseTool 鼠标绘制
AutoComplete 输入提示Scale 比例尺PolygonEditor 多边形编辑器RangingTool 测距
Driving/Transfer/Riding/Truck 路线规划ControlBar 控制罗盘RectangleEditor 矩形编辑器
Geocoder 地理编码Geolocation 定位控件CircleEditor 圆形编辑器
LineSearch 公交线路HawkEye 鹰眼控件EllipseEditor 椭圆编辑器
StationSearch 公交站点MapType 图层切换控件BezierCurveEditor 贝塞尔曲线编辑器

1、初始化地图

2、引入插件

ts
//异步加载工具条插件
AMap.plugin("AMap.Driving", function () {
  //在回调函数中实例化插件,并使用插件功能
});
物流轨迹
基本使用

1、异步加载 AMap.Driving 插件,并在回调中 new AMap.Driving() 返回drving实例

image-20250410104002961

2、经纬度可以有如下写法:

image-20250410104229094

3、关闭交通拥堵情况

image-20250410104611352

4、实际案例中画出物流轨迹

image-20250410105344630

5、实际案例中画出途径点

image-20250410105758104

自定义图标

1、关闭默认图标

image-20250410110325168

2、通过 new AMap.Marker() 自定义图标

image-20250410110737039

当前位置

image-20250410111155716

最佳缩放视野

image-20250410111528665

API
Map【
  • new AMap.Map()(div,opts)
LngLat【
  • new AMap.LngLat()(lng,lat,noWrap?),经纬度坐标。
Driving【
  • new AMap.Driving()(opts)
  • driving.search()(origin,destination,opts?,callback?)
Marker【
  • new AMap.Marker()()

项目部署

项目打包

1、执行命令打包项目

sh
pnpm build

2、